<!-- 纯净版示范 -->
<template>
    <!-- 最外层容器监mouse系列事件, 用来做节点拖拽 -->
    <div
        class="diagram-example-vue"
        id="diagram-example-vue"
        @mousedown="startNodesBus($event)"
        @mousemove="moveNodesBus($event)"
        @mouseup="endNodesBus($event)"
    >
        <!-- 左侧导航 -->
        <div class="page-left">
            
            <!-- <el-button type="danger" @click="tapToLogin" size="mini">点击登录</el-button> -->
            <!-- <el-button type="danger" @click="tapToLogin" size="mini">点击登录</el-button>
      <el-button type="success" @click="tapGetTree" size="mini">点击获取-左侧场景树</el-button>
      <el-button type="primary" @click="getEquipmentList" size="mini">点击获取-设备列表</el-button> -->
            <div class="node-title">设计元素</div>
            <div class="node-wrap">
                <div v-for="(item, i) in initNodesBasic" :key="'nodes_basic' + i" @mousedown="dragIt(item)">
                    <div class="basic-node" v-if="item.nodeType == 0 || (item.nodeType == 1 && srcTemplateOptions.length > 0)">
                        <i :class="`${item.iconClassName || 'el-icon-coin'} icon`"></i>
                        <span class="node-text">{{ item.name }}</span>
                    </div>
                </div>
                <div class="tip-wrap">
                    温馨提示：请您点击选中上方元素，拖动到右侧黑色画布，进行联线，点击线条选择所对应的源头模型或设备属性，以及对应的目标模型或设备属性！
                </div>
                <div class="tip-wrap">
                    <p>
                        运算提示：您可以选中线条之后，在输入框编辑您想要的检测公式。公式中出现的{源id}则代表源模型id或源设备id，目标id同理，不可选择非线条绑定关系的源头和目标，公式示例如下：
                    </p>
                    <p>例如：10+20*30/40+{源id}*0.3>={目标id}*0.2-10+20*30/40</p>
                </div>
            </div>
            <!-- 暂时模拟左侧场景树 -->
            <ul class="tree-ul">
                <li v-for="item in scenceTree" :key="item.id">
                    <div @click="selectScenceTree(item.id)" class="item-text">{{ item.label }}</div>
                    <div v-if="item.children && item.children.length > 0">
                        <div class="">{{ item.children.length }}</div>
                        <div
                            v-for="childrenItem in item.children"
                            :key="childrenItem.id"
                            @click="selectScenceTree(childrenItem.id)"
                            class="item-text"
                        >
                            {{ childrenItem.label }}
                        </div>
                    </div>
                </li>
            </ul>
        </div>
        <!-- 右侧表单 -->
        <div class="right-form" v-show="isShowRight">
            <!-- 22-9-28 模型基本配置 -->
            <div class="form-title">模型信息配置</div>
            <div class="form-cont">
                <template v-if="srcNode.nodeType == 1">                    
                    <!-- 源模型筛选项 -->
                    <div class="list-title">源模型筛选项</div>
                    <div class="section-cont select-list-wrap"> 
                        <el-select  
                            v-model="item.value"
                            placeholder="请选择筛选项"
                            size="mini" 
                            v-for="(item,index) in srcTemplateJson" :key="index"
                            @change="selectOptionsJsonToList({'val':$event, 'stepEq':index, 'sourceName':'srcTemplate'})" 
                        >
                            <el-option v-for="childItem in item.options" :key="childItem.id" :label="childItem.label" :value="childItem.value" ></el-option>
                        </el-select>
                    </div>
                    <div class="section-title">源模型选择</div>
                    <div class="section-cont">
                        <el-select
                            v-model="srcTemplateValue"
                            placeholder="请选择源模型"
                            @change="selectRelationOption($event, 'srcTemplate', 'srcPoint')"
                        >
                            <el-option v-for="(item, index) in srcTemplateOptions" :key="index" :label="item.label" :value="item.value" />
                        </el-select>
                    </div>
                </template>
                <template v-if="srcNode.nodeType == 0">
                    <!-- 源设备筛选项 -->
                    <div class="list-title">源设备筛选项</div>
                    <div class="section-cont select-list-wrap"> 
                        <el-select  
                            v-model="item.value"
                            placeholder="请选择设备筛选项"
                            size="mini" 
                            v-for="(item,index) in srcDeviceJson" :key="index"
                            @change="selectOptionsJsonToList({'val':$event, 'stepEq':index, 'sourceName':'srcDevice'})" 
                        >
                            <el-option v-for="childItem in item.options" :key="childItem.id" :label="childItem.label" :value="childItem.value" ></el-option>
                        </el-select>
                    </div>
                    <div class="section-title">源设备选择</div>
                    <div class="section-cont">
                        <el-select
                            v-model="srcDeviceValue"
                            placeholder="请选择源设备"
                            @change="selectRelationOption($event, 'srcDevice', 'srcPoint')"
                        >
                            <el-option v-for="(item, index) in srcDeviceOptions" :key="index" :label="item.label" :value="item.value" />
                        </el-select>
                    </div>                    
                </template>

                <div class="section-title">源监测点选择</div>
                <div class="section-cont">
                    <el-select v-model="srcPointValue" placeholder="请选择源监测点" @change="selectRelationOption($event, 'srcPoint', 'srcPoint')">
                        <el-option v-for="(item, index) in srcPointOptions" :key="index" :label="item.label" :value="item.value" />
                    </el-select>
                </div>

                <template v-if="targetNode.nodeType == 1">
                    <!-- 目标模型筛选项 -->
                    <div class="list-title">目标模型筛选项</div>
                    <div class="section-cont select-list-wrap"> 
                        <el-select  
                            v-model="item.value"
                            placeholder="请选择目标模型筛选项"
                            size="mini" 
                            v-for="(item,index) in targetTemplateJson" :key="index"
                            @change="selectOptionsJsonToList({'val':$event, 'stepEq':index, 'sourceName':'targetTemplate'})" 
                        >
                            <el-option v-for="childItem in item.options" :key="childItem.id" :label="childItem.label" :value="childItem.value" ></el-option>
                        </el-select>
                    </div>
                    <div class="section-title">目标模型</div>
                    <div class="section-cont">
                        <el-select
                            v-model="targetTemplateValue"
                            placeholder="请选择目标模型"
                            @change="selectRelationOption($event, 'targetTemplate', 'targetPoint')"
                        >
                            <el-option
                                v-for="(item, index) in targetTemplateOptions"
                                :key="index"
                                :label="item.label"
                                :value="item.value"
                            />
                        </el-select>
                    </div>
                </template>

                <template v-if="targetNode.nodeType == 0">
                    <!-- 目标设备筛选项 -->
                    <div class="list-title">目标设备筛选项</div>
                    <div class="section-cont select-list-wrap"> 
                        <el-select  
                            v-model="item.value"
                            placeholder="请选择"
                            size="mini" 
                            v-for="(item,index) in targetDeviceJson" :key="index"
                            @change="selectOptionsJsonToList({'val':$event, 'stepEq':index, 'sourceName':'targetDevice'})" 
                        >
                            <el-option v-for="childItem in item.options" :key="childItem.id" :label="childItem.label" :value="childItem.value" ></el-option>
                        </el-select>
                    </div>
                    <div class="section-title">目标设备选择</div>
                    <div class="section-cont">
                        <el-select
                            v-model="targetDeviceValue"
                            placeholder="请选择目标设备"
                            @change="selectRelationOption($event, 'targetDevice', 'targetPoint')"
                        >
                            <el-option v-for="(item, index) in targetDeviceOptions" :key="index" :label="item.label" :value="item.value" />
                        </el-select>
                    </div>
                </template>
                <div class="section-title">目标监测点选择</div>
                <div class="section-cont">
                    <el-select
                        v-model="targetPointValue"
                        placeholder="请选择目标监测点"
                        @change="selectRelationOption($event, 'targetPoint', 'targetPoint')"
                    >
                        <el-option v-for="(item, index) in targetPointOptions" :key="index" :label="item.label" :value="item.value" />
                    </el-select>
                </div>
                <!-- 自定义公式检测 -->
                <div class="rule-head">
                    <span class="flex-1 fs-12">公式如下：</span>
                    <div class="btn-item text-btn clear-btn" title="清空" @click="checkOperateKey('clear')">清空公式</div>
                    <div class="btn-item text-btn save-btn" title="保存" @click="checkOperateKey('save')">保存公式</div>
                </div>
                <div class="rule-text">{{ operateRuleObj.showRule }}</div>
                <input type="number" class="rule-input" v-model="operateRuleObj.value" ref="rule-input" />
                <div class="rule-btn-wrap">
                    <div class="btn-item text-btn" title="源id" @click="checkOperateKey('source_point_id')">源id</div>
                    <div class="btn-item text-btn" title="目标id" @click="checkOperateKey('target_point_id')">目标id</div>
                    <div class="btn-item text-btn" title="百分号" @click="checkOperateKey('0.01')">%</div>
                    <div class="btn-item symbol-btn" title="加" @click="checkOperateKey('＋')">＋</div>
                    <div class="btn-item symbol-btn" title="减" @click="checkOperateKey('－')">－</div>
                    <div class="btn-item symbol-btn" title="乘" @click="checkOperateKey('×')">×</div>
                    <div class="btn-item symbol-btn" title="除" @click="checkOperateKey('÷')">÷</div>
                    <div class="btn-item symbol-btn" title="等于" @click="checkOperateKey('＝')">＝</div>
                    <div class="btn-item symbol-btn" title="小于" @click="checkOperateKey('＜')">＜</div>
                    <div class="btn-item symbol-btn" title="小于等于" @click="checkOperateKey('≤')">≤</div>
                    <div class="btn-item symbol-btn" title="大于" @click="checkOperateKey('＞')">＞</div>
                    <div class="btn-item symbol-btn" title="大于等于" @click="checkOperateKey('≥')">≥</div>
                </div>
                <div class="rule-item"></div>
                <el-button type="primary" @click="saveBasicInfo" class="save-basic-btn">保存配置</el-button>
            </div>
        </div>
        <!-- DAG-Diagram主体 -->
        <!-- DataAll: 数据源
        updateDAG:每次动作行为会抛出最新的数据, 和对应事件名称.
        editNodeDetails: 可选内容,右键点击节点会带出节点数据,用此方法接受, 进行二次开发.比如更改节点携带的数据等.
        doSthPersonal: 属性来实现私有方法追加, 右键的面板会展示私有方法名.点击可以触发 doSthPersonal方法,  第一个参数为你定义的方法名, 第二个参数可以拿到节点id, 具体看demo即可. -->
        <DAGBoard
            ref="DAGBoard"
            :DataAll="yourJSONDataFillThere"
            @updateDAG="updateDAG"
            @editNodeDetails="editNodeDetails"
            @doSthPersonal="doSthPersonal"
        ></DAGBoard>
        <!-- 用来模拟拖拽添加的元素 -->
        <node-bus v-if="dragBus" :value="busValue.value" :pos_x="busValue.pos_x" :pos_y="busValue.pos_y" />
    </div>
</template>
<script>
// import {diagramExampleData, JSONFromService, nodesBasic,yourJSONDataFillThere} from './data.js';
import {diagramExampleData, JSONFromService, nodesBasic} from './data.js';
import {
    login,
    selectTemplatePointApi,
    templateDetail,
    scenceTreelist,
    scencePointlist,
    equipmentList,
    templateSave,
    getDeviceSelectApi, 
    getDeviceListApi
} from '@/api/model/index.js';
import {Message} from 'element-ui';
// import { scencePointlist,templateSave } from '@/api/model/index.js'

export default {
    name: 'Dag',
    components: {},
    props: {},
    data() {
        return {
            isShowRight: 0, // 是否显示右侧的编辑信息，默认不显示为0
            templateData: null, // 模型数据，如果是复制万之后，点击设计模型进入的，需要获取模型数据
            windowParams: {}, // 页面传递进来的参数
            scenceTree: [],
            data: diagramExampleData,
            defaultProps: {
                children: 'children',
                label: 'label',
            },
            initNodesBasic: nodesBasic,
            // 以下为svg必要内容
            dragBus: false, // 是否在拖拽,
            yourJSONDataFillThere: {
                // 用来展示的节点与连线
                nodes: [],
                edges: [],
            },
            // 以下为拖拽方式添加节点必须内容
            busValue: {
                value: 'name',
                pos_x: 100,
                pos_y: 100,
            },
            // 以下为表单展示数据
            formDetail: {
                currentEditNodeId: null,
                form: {
                    sides: 10,
                    nodes: 10,
                },
            },
            // 监听的事件
            onkeydown: null,
            // 复制的内容
            copyContent: [],

            initTemplateOptions: [], // 原始的模型下拉框的数据
            srcTemplateValue: '', // 源模型 id 值
            srcTemplateOptions: [], // 源模型 下拉框
            srcDeviceValue: '', // 源设备值
            srcDeviceOptions: [], // 源设备下拉框 // { value: '101', label: '源设备id-101' }
            srcPointValue: [], // 源监测点值
            srcPointOptions: [], // 源监测点下拉框 // { value: '101', label: '源监测点-101' }
            targetTemplateValue: '', // 目标模型 id 值
            targetTemplateOptions: [], // 目标模型 下拉框
            initDeviceOptions: [], // 原始的设备，下拉框的数据
            targetDeviceValue: '', // 目标设备值
            targetDeviceOptions: [], // 目标设备下拉框
            targetPointValue: [], // 目标监测点值
            targetPointOptions: [], // 目标监测点下拉框
            checkNodeInfo: {}, // 选中的元素
            checkLineInfo: {}, // 选中的线条,默认是null
            srcNode: {}, // 源头元素
            targetNode: {}, // 目标元素
            operateRuleObj: { // 自定义运算规则代码
                value: '', // input输入框里的value
                showRule: ''// 显示出来的规则
            },
            // 源设备的选择要根据 区=》厂=》站=》站=》设备，级联选择
            srcDeviceJson: [
                {
                    options:[   // 1 级别筛选
                        {
                            label:'厂区1',
                            value:1
                        }
                    ],
                    value:''
                },{
                    options:[   // 2 级别筛选
                        {
                            label:'厂区1',
                            value:1
                        }
                    ],
                    value:''
                },{
                    options:[   // 3 级别筛选
                        {
                            label:'厂区1',
                            value:1
                        }
                    ],
                    value:''
                },{
                    options:[   // 4 级别筛选
                        {
                            label:'厂区1',
                            value:1
                        }
                    ],
                    value:''
                },{
                    options:[   // 5 级别筛选
                        {
                            label:'厂区1',
                            value:1
                        }
                    ],
                    value:''
                }
            ],
             // 源模型的选择要根据 区=》厂=》站=》站=》模型，级联选择
             srcTemplateJson: [
                {
                    options:[   // 1 级别筛选
                        {
                            label:'厂区1',
                            value:1
                        }
                    ],
                    value:''
                },{
                    options:[   // 2 级别筛选
                        {
                            label:'厂区1',
                            value:1
                        }
                    ],
                    value:''
                },{
                    options:[   // 3 级别筛选
                        {
                            label:'厂区1',
                            value:1
                        }
                    ],
                    value:''
                },{
                    options:[   // 4 级别筛选
                        {
                            label:'厂区1',
                            value:1
                        }
                    ],
                    value:''
                },{
                    options:[   // 5 级别筛选
                        {
                            label:'厂区1',
                            value:1
                        }
                    ],
                    value:''
                }
            ],
            // 目标设备的选择要根据 区=》厂=》站=》站=》设备，级联选择
            targetDeviceJson: [
                {
                    options:[   // 1 级别筛选
                        {
                            label:'厂区1',
                            value:1
                        }
                    ],
                    value:''
                },{
                    options:[   // 2 级别筛选
                        {
                            label:'厂区1',
                            value:1
                        }
                    ],
                    value:''
                },{
                    options:[   // 3 级别筛选
                        {
                            label:'厂区1',
                            value:1
                        }
                    ],
                    value:''
                },{
                    options:[   // 4 级别筛选
                        {
                            label:'厂区1',
                            value:1
                        }
                    ],
                    value:''
                },{
                    options:[   // 5 级别筛选
                        {
                            label:'厂区1',
                            value:1
                        }
                    ],
                    value:''
                }
            ],
            // 目标模型的选择要根据 区=》厂=》站=》站=》模型，级联选择
            targetTemplateJson: [
                {
                    options:[   // 1 级别筛选
                        {
                            label:'厂区1',
                            value:1
                        }
                    ],
                    value:''
                },{
                    options:[   // 2 级别筛选
                        {
                            label:'厂区1',
                            value:1
                        }
                    ],
                    value:''
                },{
                    options:[   // 3 级别筛选
                        {
                            label:'厂区1',
                            value:1
                        }
                    ],
                    value:''
                },{
                    options:[   // 4 级别筛选
                        {
                            label:'厂区1',
                            value:1
                        }
                    ],
                    value:''
                },{
                    options:[   // 5 级别筛选
                        {
                            label:'厂区1',
                            value:1
                        }
                    ],
                    value:''
                }
            ] 
        };
    },
    created() {
        // this.loadJSON()
        this.onkeydown = document.addEventListener('keydown', (e) => {
            if (e.ctrlKey && e.key === 'c') {
                // 按下ctrl + c
                this.ctrlC();
            } else if (e.ctrlKey && e.key === 'v') {
                // 按下ctrl + v
                this.ctrlV();
            }
        });

        // 初始化页面参数，如果type是2，则代表修改，需要先获取模板的信息
        // 2022-11-22 yang xiao xing, 参数需要通过地址栏获取，不能通过 定义的变量 windowParams 获取
        /*let getParams = window.windowParams || {};
        this.windowParams = getParams
        console.log(`window.windowParams========`)
        console.log(window.windowParams)
        console.log(`this.windowParams========`)
        console.log(this.windowParams)
        let { modelType, templateId } = getParams;
        if (modelType == 2) {
            this.getTemplateData(templateId)
        }*/

        // console.log(templateDetail);

        // 上线时需要放开========= 
        // // 获取地址栏里边传入的参数
        this.locationSearchToJson();
        // 初始化，获取所有的设备
        this.getEquipmentList();
        // 初始化获取，所有的模型列表数据
        this.getFilterTemplateList();
        // 初始化，根据场景获取监测点的数据
        this.getScencePointlist();
        // 上线时需要放开========= end

        // 上线是需要注释掉，并把 yourJSONDataFillThere ，两个 数组里的值清空
        // this.checkLineInfo = this.yourJSONDataFillThere.edges[0]
        // 上线是需要注释掉，并把 yourJSONDataFillThere ，两个 数组里的值清空 --- end


        // 获取设备筛选项 或者模型筛选项 下拉框的所有选项
        this.getFilterDeviceOrTemplateSelectData({jb:1,stepEq:0,'sourceName':'srcDevice'})
    },
    mounted() {},
    beforeDestroy() {
        this.onkeydown = null; // 销毁事件
    },
    methods: {
        // 过滤所有空格
        trim(str = '') {
            return str.replace(/\s+/g, '');
        },
        // 切换显示右侧的编辑信息区域
        toggleRightFrom(showNum = 0) {
            this.isShowRight = showNum;
        },
        // 获取地址栏里的查询参数
        // './edit/index.html?templateId=${data.templateId}&treeId=${treeId}&type=1'
        locationSearchToJson() {
            let getHref = location.href; //取得整个地址栏
            let num = getHref.indexOf('?');
            getHref = getHref.substr(num + 1); //取得所有参数 stringvar.substr(start [, length ]

            let arr = getHref.split('&'); //各个参数放到数组里
            let paramsJson = {};
            for (let i = 0; i < arr.length; i++) {
                num = arr[i].indexOf('=');
                if (num > 0) {
                    let name = arr[i].substring(0, num);
                    let value = arr[i].substr(num + 1);
                    // this[name] = value;
                    paramsJson[name] = value;
                }
            }
            // console.log(` paramsJson ============================== `);
            // console.log(paramsJson);
            this.windowParams = paramsJson;
            // 如果有 模型id ，则利用模型id，获取模型的详细数据
            if (paramsJson && paramsJson.templateId) {
                this.getTemplateData(paramsJson.templateId);
            }
        },
        // 点击去登录
        tapToLogin() {
            // 模拟登录之后，再请求获取数据接口
            login()
                .then((res) => {
                    console.log(` toLogin res ==== `);
                    console.log(res);
                    // this.getScencePointlist();
                })
                .catch((err) => {
                    console.log(err);
                });
        },
        tapGetTree() {
            // 左侧场景树
            this.initGetScenceTreelist();
        },
        // 初始化，获取左侧场景树数据
        initGetScenceTreelist() {
            scenceTreelist()
                .then((res) => {
                    console.log(`scenceTreelist res ====== `);
                    console.log(res);
                    // 左侧场景树
                    this.scenceTree = res.data;
                })
                .catch((err) => {
                    console.log(err);
                });
        },
        // 点击左侧场景类型数目
        selectScenceTree(scenceId) {
            console.log(`scenceId: ${scenceId}`);
            this.getScencePointlist({categoryId: scenceId});
        },
        // 根据模型 id，获取该模型的信息, 得到模型数据之后，转换成可渲染的data数据
        getTemplateData(id) {
            templateDetail({templateId: id})
                .then((res) => {
                    console.log(`getTemplateData res ============== `);
                    console.log(res);
                    this.templateData = res.data;
                    this.renderTemplateToData(res.data);
                })
                .catch((err) => {
                    console.log(err);
                });
        },
        // 初始化，通过接口请求检测点数据
        getScencePointlist() {
            let {treeId} = this.windowParams;
            // treeId = treeId || 4;
            let params = {
                categoryId: treeId,
            };
            console.log(` getScencePointlist ===== categoryId: ${treeId}, `);
            scencePointlist(params)
                .then((res) => { 
                    this.renderPointOptions(res.rows);
                })
                .catch((err) => {
                    console.error(` scencePointlist error `);
                    console.log(err);
                });
        },
        // 获取所有的模型列表数据，用于下啦框选择
        getFilterTemplateList(selectJson={}) {
            let {treeId} = this.windowParams;
            // treeId = treeId || 4;
            let postParams = {
                sceneId: treeId
            };
            let initPostKey = {
                0:'zyq_id',
                1:'ejdw_id',
                2:'dw_3_id',
                3:'dw_4_id',
                4:'dw_5_id'
            }
            
            let startNum = 0;
            let jsonKey =  `${selectJson.sourceName}Json`;
            let optionsJson = this[jsonKey]
            console.log(optionsJson)
            while (startNum<=selectJson.stepEq){
                // console.log(`startNum : ${startNum}`) 
                let getPostKey = initPostKey[startNum]
                postParams[getPostKey] = optionsJson[startNum].value;
                startNum+=1;
            }
            console.log(` getFilterTemplateList postParams ========== `)
            console.log(postParams)

            selectTemplatePointApi(postParams)
                .then((res) => {
                    console.log(`selectTemplatePointApi data res 123 ============ `);
                    console.log(res);
                    let getList = [];
                    if( res.data &&  res.data.length>0){
                        res.data.forEach((item) => {
                            getList.push({
                                value : item.templateId,
                                label : item.templateName,
                                pointList:item.pointList
                            })
                            // item.value = item.templateId;
                            // item.label = item.templateName;
                        });
                    }
                    // 初始化的时候进行下拉框赋值
                    if(selectJson&&selectJson.sourceName){
                        this[`${selectJson.sourceName}Options`] = getList;
                        this[`${selectJson.sourceName}Value`] = '';
                        console.log(`selectTemplatePointApi data res 123 4444444444============ `);
                    }else{                        
                        // 保存一份数据，每次选择下拉框之后，都根据这个选择对应子集的列表
                        this.initTemplateOptions = getList;
                        this.srcTemplateOptions = getList;
                        this.targetTemplateOptions = getList;
                        // // 初始化层级筛项,
                        // this.srcTemplateJson[0].options = getList
                        // this.srcTemplateJson[0].value = '';
                        // this.tarTemplateJson[0].options = getList
                        // this.tarTemplateJson[0].value = '';
                    }
                })
                .catch((err) => {
                    console.log(err);
                });
        },
        // 获取所有的设备数据
        getEquipmentList(id = 0) {
            let params = {sceneId: id};

            equipmentList(params)
                .then((res) => {
                    console.log(`equipmentList data ============ `);
                    console.log(res);
                    this.rendeRequipmentOptions(res.data);
                })
                .catch((err) => {
                    console.log(err);
                });
        },

        // 选择完设备只有（源设备||目标设备）
        selectRelationOption(val, keyName, pointKey) {
            // console.log(`val: ${val} ,keyName: ${keyName}`);
            // console.log(`this.checkLineInfo ======= device `);
            // console.log(this.checkLineInfo);
            if (!this.checkLineInfo) {
                // console.log(`请在左侧选中线条后，在选择设备监测点属性~`);
                Message({
                    message: '请在左侧选中线条后，再选择设备监测点属性~',
                    type: 'none',
                    duration: 5 * 1000,
                });
                return false;
            }
            let {dst_input_idx, dst_node_id, id, src_node_id, src_output_idx} = this.checkLineInfo;
            console.log(
                `dst_input_idx: ${dst_input_idx} ,dst_node_id: ${dst_node_id}, id: ${id}, src_node_id: ${src_node_id}, src_output_idx: ${src_output_idx}`
            );
            let getLabel = this.returnSelectName(val, keyName).label;
            getLabel = getLabel ? getLabel : '';
            let {nodes} = this.yourJSONDataFillThere;
            for (let i = 0; i < nodes.length; i++) {
                // console.log(`i ================ 1111111111: ${i}`)
                let nodeId = nodes[i].id;
                // 如果监测点id json对象不存在，需要初始化json对象
                if (!nodes[i].pointIdJson) {
                    nodes[i].pointIdJson = {};
                }
                // 如果监测点名称json对象不存在，需要初始化json对象
                if (!nodes[i].pointNameJson) {
                    nodes[i].pointNameJson = {};
                }
                // 源头元素
                if (src_node_id == nodeId) {
                    // 设置源头模板 id
                    if (keyName == 'srcTemplate') {
                        console.log(`srcTemplate, val: ${val}，getLabel:${getLabel}`);
                        nodes[i].templateId = val; // 设置源模板 id
                        // nodes[i].name = getLabel; // 设置源模板 名称
                        this.getPointOptions(val, keyName, pointKey); // 选择完模板，获取监测点的值
                    }
                    // 设置源设备 id
                    if (keyName == 'srcDevice') {
                        console.log(`srcDevice val ${val}，getLabel:${getLabel}`);
                        nodes[i].deviceId = val; // 设置源设备 id
                        // nodes[i].name = getLabel; // 设置源设备 名称
                        this.getPointOptions(val, keyName, pointKey); // 选择完设备，获取监测点的值
                    }
                    // 源监测点
                    if (keyName == 'srcPoint') {
                        console.log(`srcPoint val ${val}，getLabel:${getLabel}`);
                        nodes[i].pointIdJson[src_output_idx] = val; // 设置监测点 id
                        nodes[i].pointNameJson[src_output_idx] = getLabel; // 设置监测点 name
                        
                        // 名称设置为设备+ 检测点名称
                        // nodes[i].name =`${nodes[i].name} ${getLabel}`;
                        nodes[i].name =`${getLabel}`;
                    }
                }
                // 目标元素
                if (dst_node_id == nodeId) {
                    // 设置目标设备 id
                    if (keyName == 'targetTemplate') {
                        nodes[i].templateId = val;
                        // nodes[i].templateOptions = this['targetTemplateOptions']; // 设置源设备 下拉数据
                        // nodes[i].name = getLabel;
                        this.getPointOptions(val, keyName, pointKey); // 选择完模板，获取监测点的值
                    }
                    // 设置目标设备 id
                    if (keyName == 'targetDevice') {
                        nodes[i].deviceId = val;
                        // nodes[i].deviceOptions = this['targetDeviceOptions']; // 设置源设备 下拉数据
                        // nodes[i].name = getLabel;
                        this.getPointOptions(val, keyName, pointKey); // 选择完设备，获取监测点的值
                    }
                    // 设置目标监测点 id
                    if (keyName == 'targetPoint') {
                        nodes[i].pointIdJson[dst_input_idx] = val; // 设置监测点 id
                        nodes[i].pointNameJson[dst_input_idx] = getLabel; // 设置监测点 name
                        
                        // 名称设置为设备+ 检测点名称
                        // nodes[i].name =`${nodes[i].name} ${getLabel}`; 
                        nodes[i].name =`${getLabel}`; 
                    }
                }
                // console.log(`i ================ 222: ${i}`)
                // console.log(nodes[i])
            }
            // 修改完之后，更新画布数据
            this.yourJSONDataFillThere.nodes = nodes;
        },
        // 根据已选择的value， 返回下啦框的 name 值
        returnSelectName(val, keyName) {
            let checkItem = null;
            let setKey = `${keyName}Options`;
            this[setKey].forEach((item) => {
                if (item.value == val) {
                    checkItem = item;
                }
            });
            // console.log(` checkItem ================ `)
            // console.log(checkItem)
            return checkItem || {};
        },
        // 选择完模型或设备之后，根据模型id 或 设备id，获取对应的监测点下拉框数据
        getPointOptions(val, keyName, pointKey) {
            console.log(` keyName: ${keyName} `)
            // let setKey = `${keyName}Options`;
            // let setKey = `${keyName}List`;
            let newArr = [];

            // 传入的值，与默认存储的设备对应的监测点，或模型对应的检测点列表的json关联表
            let optionKeyJson = {
                'srcDevice':'initDeviceOptions' ,
                'targetDevice':'initDeviceOptions' , 
                'srcTemplate':'initTemplateOptions' , 
                'targetTemplate':'initTemplateOptions'  
            }
            let getKey = optionKeyJson[keyName]
            let initOptionsList = this[getKey]; 
            // 通过与默认备份的数据对比，获取对应的包含的监测点列表
            if(initOptionsList && initOptionsList.length>0){
                initOptionsList.forEach((item) => {
                    if (item.value == val) {
                        console.log(`val : ${val}`);
                        console.log(item);
                        newArr = item.pointList;
                    }
                });                
            }

            let pointArr = []; // 监测点数组
            newArr.forEach((item) => {
                pointArr.push({
                    ...item,
                    label: item.pointName,
                    value: item.pointId,
                });
            });
            this[`${pointKey}Value`] = '';
            this[`${pointKey}Options`] = pointArr;
        },
        // 选择完右侧的下啦框之后，更新画布上数据节点的值
        selectToUpdateJsonData(params) {
            if (!this.checkNodeInfo) {
                Message({
                    message: '请在左侧选中设备后，在选择您要更改的设备',
                    type: 'error',
                    duration: 5 * 1000,
                });
                return;
            }
            this.checkNodeInfo = Object.assign(this.checkNodeInfo, params);
            let nodesNata = this.yourJSONDataFillThere.nodes;
            for (let i = 0; i < nodesNata.length; i++) {
                if (nodesNata[i].id == this.checkNodeInfo.id) {
                    nodesNata[i] = this.checkNodeInfo;
                    break;
                }
            }
            this.yourJSONDataFillThere.nodes = nodesNata;
        },
        // 把根据模型id，获取到的模型数据，转换成可在画布上显示的数据，（线条、和元素）
        renderTemplateToData(data) {
            let {edges, nodes} = data.template;
            this.yourJSONDataFillThere.edges = edges ? JSON.parse(edges) : [];
            this.yourJSONDataFillThere.nodes = nodes ? JSON.parse(nodes) : [];
        },
        // 得到设备数据之后，转换成下拉框可用的数据
        rendeRequipmentOptions(data) {
            console.log(' 得到设备数据之后，转换成下拉框可用的数据 rendeRequipmentOptions =========== ');
            if (!data || data.length < 1) {
                console.log('暂无数据，不需要渲染');
                return false;
            }
            let newArr = data || [];
            if (data.length > 100) {
                newArr = data.slice(0, 100);
            }
            // console.log(`1、 data : ${data.length} , newArr: ${newArr.length}`)
            newArr.forEach((item, index) => {
                // console.log(item)
                newArr[index] = Object.assign(newArr[index], {
                    pointId: item.equipmentId,
                    value: item.equipmentId,
                    name: item.equipmentName,
                    label: item.equipmentName,
                });
            });
            // this.pointOptions = newArr;
            // 保存原始的设备下拉框的所有数据
            this.initDeviceOptions = newArr;
            this.deviceOptions = newArr;
            this.srcDeviceOptions = newArr;
            this.targetDeviceOptions = newArr;
        },
        // 得到监测点数据之后，转换成下拉框可用的数据
        renderPointOptions(data) {
            let newArr = [];
            /* if (data.length > 10) {
                newArr = data.slice(0, 10);
            } */
            console.log(`1、 data : ${data.length} , newArr: ${newArr.length}`);
            newArr.forEach((item, index) => {
                console.log(item);

                newArr[index] = Object.assign(newArr[index], {
                    pointId: item.pointId,
                    value: item.pointId,
                    name: item.pointName,
                    label: item.pointName,
                });
            });
            // this.pointOptions = newArr;
            this.observationPointOptions = newArr;
        },
        generate() {
            // 生成随机图
            const detaultNumber = 100; // 默认的数量
            const nodesNumber = this.formDetail.form.nodes || 10; // 节点数量
            const sidesNumber = this.formDetail.form.sides || 10; // 边的数量
            const GRAPH_WIDTH = (nodesNumber > detaultNumber ? nodesNumber : detaultNumber) * 10; // 图的宽度
            const GRAPH_HEIGHT = (nodesNumber > detaultNumber ? nodesNumber : detaultNumber) * 10; // 图的高度
            const newGraphNodes = [];
            const newGraphSides = [];
            this.yourJSONDataFillThere = {
                nodes: [],
                edges: [],
            };
            // step1 生成节点
            const generatePosition = (eq) => {
                // 生成坐标
                const PosX = (Math.random() * GRAPH_WIDTH).toFixed();
                const PosY = (Math.random() * GRAPH_HEIGHT).toFixed();
                console.log(`eq: ${eq}, PosX : ${PosX},PosY : ${PosY}`);
                return {
                    pos_x: Number(PosX),
                    pos_y: Number(PosY),
                };
            };
            for (let nodeIndex = 0; nodeIndex < nodesNumber; nodeIndex++) {
                const eachGraphNode = {
                    id: nodeIndex + 1,
                    in_ports: [0],
                    name: `NODE_${nodeIndex}`,
                    out_ports: [0],
                    detail: [],
                    ...generatePosition(nodeIndex),
                };
                newGraphNodes.push(eachGraphNode);
            }
            this.yourJSONDataFillThere.nodes = newGraphNodes;
            // step2 生成边 因为id是有序的,所以这里id范围根据节点生成
            const generateNodeEntry = (nodesMax) => {
                return (Math.random() * nodesMax).toFixed();
            };
            for (let sideIndex = 0; sideIndex < sidesNumber; sideIndex++) {
                const eachSide = {
                    dst_input_idx: 0,
                    dst_node_id: Number(generateNodeEntry(nodesNumber)) || 1,
                    id: sideIndex + 1,
                    src_node_id: Number(generateNodeEntry(nodesNumber)) || 1,
                    src_output_idx: 0,
                    edgesText: '',
                };
                if (eachSide.dst_node_id !== eachSide.src_node_id) {
                    newGraphSides.push(eachSide);
                }
            }
            this.yourJSONDataFillThere.edges = newGraphSides;
            console.log(JSON.stringify(this.yourJSONDataFillThere, null, 4));
        },
        autoLayout() {
            // 自动布局
            const allSidesArr = this.yourJSONDataFillThere.edges.map((i) => [i.src_node_id, i.dst_node_id]); // [[1,2], [2,3]]
            const connectTest = (a, b) => {
                // 测试关联 [1, 2], [2, 3]
                if (a[0] === b[1] || b[0] === a[1] || a[0] === b[0] || a[1] === b[1]) {
                    return true;
                } else {
                    return false;
                }
            };
            let arrSeparate = [[allSidesArr.shift()]];
            const testArr = (allSidesArr) => {
                // 递归检测连线的关系,把有关联的连线放在一起
                for (let i = 0; i < arrSeparate[arrSeparate.length - 1].length; i++) {
                    for (let j = 0; j < allSidesArr.length; j++) {
                        if (connectTest(arrSeparate[arrSeparate.length - 1][i], allSidesArr[j])) {
                            // 建立关联,放入分组
                            arrSeparate[arrSeparate.length - 1] = arrSeparate[arrSeparate.length - 1].concat(allSidesArr.splice(j, 1));
                            testArr(allSidesArr); // 递归
                            return false;
                        }
                    }
                }
                // 循环完毕仍然有数据的话, 放入第二个数组
                if (allSidesArr.length) {
                    arrSeparate.push(allSidesArr.shift());
                    testArr(allSidesArr);
                }
            };
            testArr(allSidesArr);
            console.log('整理好的数据', arrSeparate);
            // while (arrSeparate.length) { // 递归画所有的关系图
            //   this.drawSingleGraph(arrSeparate.shift());
            // }
            this.drawSingleGraph(arrSeparate.shift()); // 先画一个
        },
        unique(arr) {
            // 数组去重
            return Array.from(new Set(arr));
        },
        drawSingleGraph(graphArr) {
            const left = graphArr.map((v) => v[0]); // 出口集合
            const right = graphArr.map((v) => v[1]); // 入口集合
            const result = [[]];
            // 寻找最上层节点 只有出口没有入口的都是顶点
            result[0] = left.filter((v) => right.indexOf(v) === -1);
            // 没有上类节点的图证明成环,放弃渲染 有顶点才会渲染
            let recursionArr = graphArr; // 全部数组  [[1,2], [2,3], [3,4]]...
            if (result[0].length) {
                result[0] = this.unique(result[0]);
                while (recursionArr.length) {
                    // 剩余连线存在就一直递归
                    // recursionArr = graphArr.filter(eachArr => result[0].indexOf(eachArr[0]) === -1); // 更新剩余的边
                    let nextLevelNodesArr = [];
                    /* result[result.length - 1].map((sourceFromId) => { // 当前循环到的层级
            recursionArr.map(eachArr => {
              if (result[result.length - 1].indexOf(eachArr[0]) > -1) { // 检测到入口对应的边,把出口id放入数组下一级
                 nextLevelNodesArr.push(eachArr[1])
              }
            })
          }) */
                    result[result.length - 1].map(() => {
                        // 当前循环到的层级
                        recursionArr.map((eachArr) => {
                            if (result[result.length - 1].indexOf(eachArr[0]) > -1) {
                                // 检测到入口对应的边,把出口id放入数组下一级
                                nextLevelNodesArr.push(eachArr[1]);
                            }
                        });
                    });
                    recursionArr = recursionArr.filter((eachArr) => result[result.length - 1].indexOf(eachArr[0]) === -1); // 更新剩余的边
                    if (nextLevelNodesArr.length) {
                        nextLevelNodesArr = this.unique(nextLevelNodesArr);
                        result[result.length] = nextLevelNodesArr;
                    }
                }
            }
            // debugger
            this.draw(result);
        },
        draw(nodesIdArr) {
            // const newNodesJSON = [];
            const CANVAS_WIDTH = 1440;
            const EACH_LEVEL_HIGH = 300;
            this.yourJSONDataFillThere.nodes.map((v) => {
                v.pos_x = -1000;
                v.pox_y = 0;
            });
            nodesIdArr.forEach((row, rowIndex) => {
                row.map((curId, curIndex) => {
                    const aim = this.yourJSONDataFillThere.nodes.find((v) => v.id === curId);
                    aim.pos_x = (CANVAS_WIDTH / row.length + 1) * (curIndex + 1);
                    aim.pos_y = EACH_LEVEL_HIGH * (rowIndex + 1);
                });
            });
        },
        ctrlC() {
            let currentChoice = this.$refs.DAGBoard.choice;
            if (currentChoice.index > -1) {
                // 有选中元素
                let activeNodes = this.yourJSONDataFillThere.nodes.filter((item) => currentChoice.paneNode.indexOf(item.id) > -1);
                this.copyContent = JSON.parse(JSON.stringify(activeNodes));
                this.copyContent.forEach((item) => {
                    item.id = item.id + this.yourJSONDataFillThere.nodes.length + 100; // 自定义id规范 这里随便写个长度+100作为id
                    item.pos_x += 100 / (sessionStorage['svgScale'] || 1);
                    item.pos_y += 100 / (sessionStorage['svgScale'] || 1);
                });
            }
        },
        ctrlV() {
            if (!this.copyContent.length) return false;
            this.yourJSONDataFillThere.nodes.push(...this.copyContent);
            this.$refs.DAGBoard.choice = {
                paneNode: [], // 选取的节点下标组
                index: -1,
                point: -1,
            }; // 复制完成 取消选中状态
            this.copyContent = [];
        },
        updateDAG(data, action, id) {
            // console.log(`updateDAG ======================= action: ${action} `)
            // console.log(...arguments)
            // console.log(JSON.stringify(data))
            switch (action) {
                case 'selectNode': //
                    this.showNodeDetails(data.nodes.find((item) => item.id === id));
                    break;
                case 'focusCurNode': // 选中元素，展示右侧下啦框，选择之后绑定关联关系
                    // console.log(`data : ${data}, action : ${action}, id: ${id}`)
                    // console.log(`data : ${data}, action : ${action}, id: ${id}`)
                    // this.showNodeDetails(data.nodes.find(item => item.id === id));
                    this.checkNodeRightAttribute(data);
                    break;
                case 'checkCurPath': // 选中线条，展示右侧下啦框，选择之后绑定关联关系
                    this.checkLineRightAttribute(data);
                    break;

                default:
                    () => {};
            }
        },
        editNodeDetails() {
            console.log('editNodeDetails ======================= ');
            console.log(...arguments);
        },
        doSthPersonal() {
            console.log('doSthPersonal ======================= ');
            console.log(...arguments);
        },
        loadJSON() {
            // 这里可以跟服务端交互获取数据
            setTimeout(() => {
                this.yourJSONDataFillThere = JSONFromService;
            }, 500);
        },
        // 获取经过计算的 x y 的坐标
        getNodesXY(e) {
            return {
                x: e.pageX,
                y: e.pageY,
            };
        },
        /**
         * 通过拖拽方式加入新节点必须的函数
         */
        startNodesBus(e) {
            /**
             *  别的组件调用时, 先放入缓存
             * dragDes: {
             *    drag: true,
             *    name: 组件名称
             *    type: 组件类型
             *    model_id: 跟后台交互使用
             * }
             **/
            let dragDes = null;
            if (sessionStorage['dragDes']) {
                dragDes = JSON.parse(sessionStorage['dragDes']);
            }
            if (dragDes && dragDes.drag) {
                // const x = e.pageX;
                // const y = e.pageY;

                const x = this.getNodesXY(e).x;
                const y = this.getNodesXY(e).y;
                this.busValue = Object.assign({}, this.busValue, {
                    pos_x: x,
                    pos_y: y,
                    value: dragDes.name,
                });
                this.dragBus = true;
            }
        },
        moveNodesBus(e) {
            // 移动模拟节点
            if (this.dragBus) {
                const x = e.pageX;
                const y = e.pageY;
                // console.log(`moveNodesBus x: ${x}, y:${y}`)
                this.busValue = Object.assign({}, this.busValue, {
                    pos_x: x,
                    pos_y: y,
                });
            }
        },
        endNodesBus(e) {
            // 节点放入svg
            let dragDes = null;
            if (sessionStorage['dragDes']) {
                dragDes = JSON.parse(sessionStorage['dragDes']);
            }
            if (dragDes && dragDes.drag && e.toElement.id === 'svgContent') {
                const pos_x = (e.offsetX - 90 - (sessionStorage['svg_left'] || 0)) / (sessionStorage['svgScale'] || 1); // 参数修正
                const pos_y = (e.offsetY - 15 - (sessionStorage['svg_top'] || 0)) / (sessionStorage['svgScale'] || 1); // 参数修正
                delete dragDes.drag;
                const params = {
                    model_id: sessionStorage['newGraph'],
                    desp: {
                        pos_x,
                        pos_y,
                        name: this.busValue.value,
                        ...dragDes,
                    },
                };
                // console.log(`params.desp ================== `)
                // console.log(params.desp)
                this.yourJSONDataFillThere.nodes.push({
                    ...params.desp,
                    id: this.yourJSONDataFillThere.nodes.length + 100, // 这里注意, 新增的id一定不能重复, 建议id交由后端处理
                    in_ports: [0],
                    out_ports: [0],
                });
                // out_ports: params.desp.out_ports?params.desp.out_ports:[0] // 如有有传入值，则重新设置出点
            }
            window.sessionStorage['dragDes'] = null;
            this.dragBus = false;
        },
        dragIt(val) {
            val.form.createTime = new Date().toDateString();
            sessionStorage['dragDes'] = JSON.stringify({
                drag: true,
                ...val,
            });
        },
        /**
         * 右侧form展示逻辑
         */
        showNodeDetails(val) {
            // 展示选中的节点
            const {id, form} = val;
            if (!form) return;
            this.formDetail = {
                currentEditNodeId: id,
                form: Object.assign(this.formDetail.form, form, {}),
            };
        },
        // 点击右侧基本设置的保存
        saveBasicInfo() {
            let {edges, nodes} = this.yourJSONDataFillThere;
            console.log(`edges : ${edges.length}, nodes: ${nodes.length}`);
            // let nodesIdArr = nodes.map(item=> item.id);
            // let relationNumer = 0; // 所有的设备是否都关联了，如果都关联了，个数应该等于设备数
            let relationJson = {}; // 判断所有的设备是不是都关联了
            // let isAllRelation = true; // 所有的node都连接起来了。
            let msgStr = ''; //定义提示语，有提示语不能提交
            for (let i = 0; i < nodes.length; i++) {
                let getId = nodes[i].id;
                let isAbout = false; //是否有线连接。
                for (let j = 0; j < edges.length; j++) {
                    edges[j].isCheck = false;// 提交的时候，所有的线的选中状态需要取消掉
                    edges[j].type = '';// 提交的时候，所有的线的选中状态需要取消掉

                    let {dst_node_id, src_node_id} = edges[j];
                    // console.log(`dst_node_id:${dst_node_id},src_node_id:${src_node_id},getId:${getId}`)
                    let keyStr = `${getId}`;
                    if (!relationJson[keyStr]) {
                        relationJson[keyStr] = {};
                    }
                    if (dst_node_id == keyStr) {
                        relationJson[keyStr]['dst_node_id'] = relationJson[keyStr]['dst_node_id']
                            ? relationJson[keyStr]['dst_node_id'] + 1
                            : 1;
                    }
                    if (src_node_id == keyStr) {
                        relationJson[keyStr]['src_node_id'] = relationJson[keyStr]['src_node_id']
                            ? relationJson[keyStr]['src_node_id'] + 1
                            : 1;
                    }
                    // 必须有一个进入点或者出点， 并且入点和出点的id 不能相同
                    if ((dst_node_id == keyStr || src_node_id == keyStr) && dst_node_id != src_node_id) {
                        isAbout = true;
                        // 如果线的源头id，等于node元素的id，并且没有出点的时候，说明是源头
                        if (src_node_id == keyStr) {
                            relationJson[keyStr]['sourceTop'] = 1;
                        }
                        // 只有出点，说明是头部元素
                        if (src_node_id == keyStr) {
                            relationJson[keyStr]['targetEnd'] = 1;
                        }
                    }
                }
                if (!isAbout) {
                    msgStr = '有未关联的设备，请您关联后再提交~';
                    break;
                }
            }

            console.log(`relationJson data ===================== `);
            console.log(relationJson);
            // 判断是不是有多个起始点（只有出点，没有入店）
            let topNodeNumber = 0; // 源点个数，
            let topNodeId = 0; // 源点 id
            let endArr = []; // 节点数组，可能存在多个结束点，所以需要用数组记录一下
            for (let key in relationJson) {
                if (relationJson[key]['src_node_id'] && !relationJson[key]['dst_node_id']) {
                    topNodeNumber += 1;
                    topNodeId = key;
                }
                if (relationJson[key]['dst_node_id'] && !relationJson[key]['src_node_id']) {
                    endArr.push(Number(key));
                }
            }
            if (topNodeNumber > 1) {
                msgStr = '您的模型可能存在多个源点，请您修改后再保存~';
            }

            // 有提示语，不能提交
            if (msgStr) {
                Message({
                    message: msgStr,
                    type: 'error',
                    duration: 2 * 1000,
                });
                return;
            }

            let {treeId, templateId} = this.windowParams || {};
            let params = {
                template: {
                    // templateName: '测试模型'+ new Date(), // string 模板名称
                    templateId: templateId, // 模型 id
                    status: '', // string 状态
                    sceneId: treeId, // integer 场景id
                    edges: JSON.stringify(edges), // 用于前端显示的线， 数组形式
                    nodes: JSON.stringify(nodes), // 用于前端展示的元素 ，数组形式  代表模型里的设备
                },
                templateDetailList: [], // 模型的每一组（源头=》目标）对应的数据
            }
            console.log(`save params 123================= `)
            console.log(this.windowParams)
            console.log(`treeId, ${treeId},templateId, ${ templateId} `)
            console.log(params)

            // 0: 设备, 1: 模型
            // sourceType: string //源类型,模型,设备
            // targetType: string//目标类型,模型,设备
            //
            //  sourceTop:  // 是否为头部  =》 1：是；0：否
            //  targetEnd: // 是否为尾部 =》 1：是；0：否
            let listData = [];
            for (let i = 0; i < edges.length; i++) {
                let itemJSON = {
                    sourceEquipmentId: edges[i].src_node_id, // 源设备id
                    targetEquipmentId: edges[i].dst_node_id, // integer 目标设备id
                }; 
                // console.log(pointJson)
                // 22-9-27
                for (let j = 0; j < nodes.length; j++) {
                    let pointJson = nodes[j]['pointIdJson']; 
                    if (!pointJson) {
                        msgStr = '请您选择右侧的关联关系~';
                        break;
                    } 
                    // 获取当前节点的元素类型，0：设备； 1：模型
                    let getType = nodes[j].nodeType;
                    // 设置源头设备的 x y 的坐标，以及源监测点属性
                    if (nodes[j].id == itemJSON.sourceEquipmentId) {
                        // 添加源头节点特殊标识， type:1,代表源头
                        if (topNodeId == nodes[j].id) {
                            itemJSON['sourceTop'] = 1;
                            // itemJSON['sourceType']
                        }
                        itemJSON['sourceEquipmentLocaionX'] = nodes[j].pos_x; // integer 源设备x轴坐标
                        itemJSON['sourceEquipmentLocaionY'] = nodes[j].pos_y; // integer 源设备y轴坐标
                        itemJSON['sourcePointId'] = nodes[j].pointId;
                        itemJSON['sourceType'] = getType;
                        // 22-9-27
                        // 根据索引获取 pointJson 对应的监测点id
                        itemJSON['sourcePointId'] = pointJson[edges[i].src_output_idx];

                        // 需要吧id改成正确的 模板id 或 设备id
                        itemJSON['sourceEquipmentId'] = getType == 1 ? nodes[j].templateId : nodes[j].deviceId;
                    } else {
                        itemJSON['targetEquipmentLocaionX'] = nodes[j].pos_x; // integer 目标设备x轴坐标
                        itemJSON['targetEquipmentLocaionY'] = nodes[j].pos_y; // integer 目标设备y轴坐标
                        itemJSON['targetPointId'] = nodes[j].pointId;
                        itemJSON['targetType'] = getType;
                        // 22-9-27
                        // 根据索引获取 pointJson 对应的监测点id
                        itemJSON['targetPointId'] = pointJson[edges[i].dst_input_idx];

                        // 需要吧id改成正确的 模板id 或 设备id
                        itemJSON['targetEquipmentId'] = getType == 1 ? nodes[j].templateId : nodes[j].deviceId;
                        console.log(`nodes[j].id:${nodes[j].id},itemJSON.targetEquipmentId:${itemJSON.targetEquipmentId}`);
                        // console.log(endArr)
                        // 如果当前元素在，结束数组中包含着，则设置结束标识
                        if (endArr.indexOf(Number(itemJSON.targetEquipmentId)) >= 0) { 
                            itemJSON['targetEnd'] = 1;
                        }
                    }
                } 
                // 调用把字符串转成可执行的公式
                itemJSON = this.textToOperate(itemJSON, edges[i]);
                listData.push(itemJSON);
            }
            params.templateDetailList = listData;
            console.log(` 点击右侧的保存配置按钮 == listData `);
            console.log(listData);
            // console.log(endArr)

            // 有提示语，不能提交
            if (msgStr) {
                Message({
                    message: msgStr,
                    type: 'error',
                    duration: 2 * 1000,
                });
                return;
            }

            templateSave(params)
                .then((res) => {
                    console.log(` templateSave res ok `);
                    console.log(res);
                    if (res.code == 0) {
                        Message({
                            message: res.msg ? res.msg : '保存成功~',
                            duration: 2 * 1000,
                        });
                    }
                })
                .catch((err) => {
                    console.error(` templateSave error `);
                    console.log(err);
                });
        },
        // 选中对应的元素，需要把该元素的属性值在右侧展示
        checkNodeRightAttribute(data) {
            if (!data) {
                return;
            }
            let nodeInfo = null;
            data.nodes.forEach((item) => {
                if (item.checkCurNode) {
                    nodeInfo = item;
                }
            });
            this.yourJSONDataFillThere = data;
            this.updateRightNodeAttribute(nodeInfo);
        },
        // 选中线条之后，选择源头和目标的设备id 和监测点属性
        checkLineRightAttribute(data) {
            console.log(`checkLineRightAttribute data=======`)
            console.log(data)
            let lineInfo = null;
            data.edges.forEach((item) => {
                if (item.isCheck) {
                    lineInfo = item;
                } 
            });

            /*for (let i = data.edges.length - 1; i >= 0; i--) {
                if (!lineInfo && data.edges[i].isCheck) {
                    lineInfo = data.edges[i]; 
                }
                data.edges[i].isCheck=false;
            }*/
            this.checkLineInfo = lineInfo;
            this.yourJSONDataFillThere = data;
            let srcNode = null, // 源头元素
                targetNode = null; // 目标指向元素
            if (!lineInfo) {
                return;
            }
            this.toggleRightFrom(1); // 显示右侧
            let {nodes} = this.yourJSONDataFillThere;
            nodes.forEach((item, i) => {
                item.index = i;
                // 源头元素
                if (lineInfo.src_node_id == item.id) {
                    srcNode = item;
                }
                // 目标元素
                if (lineInfo.dst_node_id == item.id) {
                    targetNode = item;
                }
            });
            this.srcNode = srcNode;
            this.targetNode = targetNode;
            // 设置源头的 模型 id ，或者 设备 id， 和监测点 id
            if (srcNode.nodeType == 1) {
                this.srcTemplateValue = srcNode.templateId ? srcNode.templateId : '';
                this.getSelectOptions(srcNode.deviceId, 'srcTemplateOptions', 'srcPointOptions'); 
            } else {
                this.srcDeviceValue = srcNode.deviceId ? srcNode.deviceId : '';
                this.getSelectOptions(srcNode.deviceId, 'srcDeviceOptions', 'srcPointOptions'); 
            }
            console.log(` this.srcDeviceValue: ${ this.srcDeviceValue }，this.srcTemplateValue: ${this.srcTemplateValue}`)
            // 根据选择线条的索引设置检测的id值 （下啦框的value）
            let srcIndex = lineInfo.src_output_idx;
            this.srcPointValue = '';
            if (srcNode.pointIdJson) {
                this.srcPointValue = srcNode.pointIdJson[srcIndex] ? srcNode.pointIdJson[srcIndex] : '';
            }

            // 设置目标的 模型 id ，或者 设备 id
            if (targetNode.nodeType == 1) {
                this.targetTemplateValue = targetNode.templateId ? targetNode.templateId : '';
                this.getSelectOptions(targetNode.templateId, 'targetTemplateOptions', 'targetPointOptions');
            } else {
                this.targetDeviceValue = targetNode.deviceId ? targetNode.deviceId : '';
                this.getSelectOptions(targetNode.deviceId, 'targetDeviceOptions', 'targetPointOptions');
            }
            // 根据选择线条的索引设置检测的id值 （下啦框的value）
            let targetIndex = lineInfo.dst_input_idx;
            this.targetPointValue = '';
            if (targetNode.pointIdJson) {
                this.targetPointValue = targetNode.pointIdJson[targetIndex] ? targetNode.pointIdJson[targetIndex] : '';
            }
            // 如果选中的线条有设置公式，则需要显示把公式显示出来
            this.operateRuleObj = {
                value: '', // input输入框里的value
                showRule: ''// 显示出来的规则
            }
            if(lineInfo.operateRuleObj){
                this.operateRuleObj = lineInfo.operateRuleObj;
            }
        },
        // 根据现在选择的下拉框值，获取对应的下级的options 列表
        getSelectOptions(val, parentSelectKey, pointOptionsKey) {
            // 如果没有 选择的value 值，则不需要便利
            if (!val) {
                return;
            }
            let deviceList = this.initDeviceOptions;
            let templateList = this.initTemplateOptions;
            // 如果是模型， 源头是模型 或者 目标是模型
            if (parentSelectKey == 'srcTemplateOptions' || parentSelectKey == 'targetTemplateOptions') {
                let checkIndex = -1;
                let checkItem = templateList.filter((item, eq) => {
                    if (item.value == val) {
                        checkIndex = eq;
                        return item.pointList;
                    }
                });
                if (checkItem.length > 0) {
                    let pointList = this.setOptionLabelValue(checkItem[0].pointList);
                    this.initTemplateOptions[checkIndex].pointList = pointList;
                    this[pointOptionsKey] = pointList;
                }
            }
            // 如果是设备 ，源头是设备 或者 目标是设备
            if (parentSelectKey == 'srcDeviceOptions' || parentSelectKey == 'targetDeviceOptions') {
                let checkIndex = -1;
                let checkItem = deviceList.filter((item, eq) => {
                    if (item.value == val) {
                        checkIndex = eq;
                        return item.pointList;
                    }
                });
                if (checkItem.length > 0) {
                    let pointList = this.setOptionLabelValue(checkItem[0].pointList);
                    this.initDeviceOptions[checkIndex].pointList = pointList;
                    this[pointOptionsKey] = pointList;
                }
            }

            console.log(`val : ${val}, parentSelectKey: ${parentSelectKey},pointOptionsKey: ${pointOptionsKey}`);
        },
        setOptionLabelValue(arr) {
            let pointArr = []; // 监测点数组
            arr.forEach((item) => {
                pointArr.push({
                    ...item,
                    label: item.pointName,
                    value: item.pointId,
                });
            });
            // console.log(` 22222222 pointArr ======= `);
            // console.log(pointArr);
            return pointArr;
        },
        // 更新选中点的右侧数据的展示
        updateRightNodeAttribute(nodeInfo) {
            this.checkNodeInfo = nodeInfo;
            // deviceId: 设备id，如果有设备id，则把右侧的设备value值对应改变一下
            this.deviceValue = nodeInfo.deviceId ? nodeInfo.deviceId : '';
            // pointId：监测点 id，如果有监测点id，则把右侧的监测点value值对应改变一下
            this.observationPointValue = nodeInfo.pointId ? nodeInfo.pointId : '';
        },
        // 点击运算符号的时候
        checkOperateKey(keyText) {
            let symbolArr = ['＋', '－', '×', '÷', '＝', '＜', '≤', '＞', '≥'];
            let idArr = ['source_point_id', 'target_point_id'];
            console.log(symbolArr);
            console.log(idArr);
            console.log(`keyText: ${keyText}`);
            let getValue = this.trim(this.operateRuleObj.value);
            let getRule = this.operateRuleObj.showRule;
            let setText = '';

            // 如果点击的是保存
            if (keyText == 'save') {
                if (!this.checkLineInfo) {
                    // console.log(`请在左侧选中线条后，在选择设备监测点属性~`);
                    Message({
                        message: '请在左侧选中线条后，再选择设备监测点属性~',
                        type: 'none',
                        duration: 5 * 1000,
                    });
                    return false;
                }
                // console.log(`this.checkLineInfo ===============`);
                // console.log(this.checkLineInfo);
                // this.operateRuleObj.value = '';
                this.checkLineInfo.operateRuleObj = this.operateRuleObj;
                // this.operateRuleObj.showRule = '';

                let {edges} = this.yourJSONDataFillThere;
                for (let i = 0; i < edges.length; i++) {
                    if (edges[i].id == this.checkLineInfo.id) {
                        this.yourJSONDataFillThere.edges[i] = this.checkLineInfo;
                        break;
                    }
                }
                Message({
                    message: '保存公式成功~',
                    type: 'none'
                });
                return;
            }
            // 如果点击的是清空
            if (keyText == 'clear') {
                this.operateRuleObj.showRule = '';
                this.operateRuleObj.value = '';
                this.checkLineInfo.operateRuleObj = this.operateRuleObj;
                return;
            }

            // 如果是运算符号，则需要显示到公式栏里
            if (symbolArr.includes(keyText)) {
                // 字符串拼接的时候，判断是否可以直接拼接，如果输入框里有内容，可以直接拼接，否则替换最后一位
                if (getValue == '') {
                    // 已有公式不为空，则判断最后一位是否为符号
                    if (getRule != '') {
                        // 最后一位是符号，则需要替换一下最后一个的符号
                        if (symbolArr.includes(getRule.substr(-1))) {
                            setText = `${getRule.substr(0, getRule.length - 1)}${keyText}`;
                        } else {
                            setText = `${getRule}${keyText}`;
                        }
                    }
                } else {
                    // 输入框不为空，则直接拼接
                    setText = `${getRule}${getValue}${keyText}`;
                }
                this.operateRuleObj.value = '';
            } else if (idArr.includes(keyText)) {
                // 如果选择的是源id或目标id
                // setText=`{${keyText}}`;
                // 已有公式不为空，则判断最后一位是否为符号
                if (getRule == '') {
                    setText = `{${keyText}}`;
                } else {
                    // 如果最后一位是加减乘除的符号，则直接添加
                    if (symbolArr.includes(getRule.substr(-1))) {
                        setText = `${getRule}{${keyText}}`;
                    }
                    // 如果最后一位是，源id 或目标id，则进行替换
                    if (getRule.substr(-1) == '}') {
                        let lastId = getRule.lastIndexOf('{');
                        setText = `${getRule.substr(0, lastId)}{${keyText}}`;
                    }
                }
            } else {
                // 百分比运算
                // 只有当 value 大于0的时候，才可以运行,
                // 或者公式的最后一位是id 的时候才可以
                if (getValue > 0 || getRule.substr(-1) == '}') {
                    setText = `${getRule}${getValue}×${keyText}`;
                }
                // 如果公式空，或者是 0.01， 则直接覆盖
                if (getRule == '' || getRule == '%') {
                    setText = `${keyText}`;
                }                
                this.operateRuleObj.value = '';
            }
            this.operateRuleObj.showRule = setText;
            console.log(this.$refs['rule-input']);
            // operateRuleObj.showRule}}</div>
            // <input type="number" class="rule-input" v-model="operateRuleObj.value"
        },
        // 把字符串转成可执行的运算公式
        textToOperate(itemJSON, edgeJSON) {
            let operateRuleObj = edgeJSON.operateRuleObj;
            if (!operateRuleObj || operateRuleObj.showRule == '') {
                return itemJSON;
            }

            // 得到可用直接运行计算的运算公式
            let ruleText = operateRuleObj.showRule;
            ruleText = ruleText.replace(/\+/g, '+');
            ruleText = ruleText.replace(/－/g, '-');
            ruleText = ruleText.replace(/÷/g, '/');
            ruleText = ruleText.replace(/×/g, '*');
            ruleText = ruleText.replace(/≥/g, '>=');
            ruleText = ruleText.replace(/＞/g, '>');
            ruleText = ruleText.replace(/≤/g, '<=');
            ruleText = ruleText.replace(/＜/g, '<');
            // split 利用正则分隔成数组；
            let ruleArr = ruleText.split( /[>|<(>=)|(<=)]/); 
            // 获取截取数组的长度
            let ruleLen = ruleArr.length;
            if (ruleLen > 0) {
                itemJSON.operateRuleObj = {
                    showRule: operateRuleObj.showRule,
                    logicalOperation: ruleText.substring(ruleArr[0].length,ruleText.length-ruleArr[ruleLen-1].length),// '>',
                    leftFormula: ruleArr[0], //'10*20%-5+34+{source_point_id}',
                    rightFormula: ruleArr[ruleLen-1], // '10*20%-5+{target_point_id}',
                    formulaText: ruleText //'10*20%-5+34+{source_point_id}>10*20%-5+{target_point_id} ',
                };
            }
            return itemJSON; 
        },
        // 通过接口获取数据之后，把数据转换成select可用的数据
        getListToOptions(list){
            let optionsArr = []             
            list.forEach(item=>{
                optionsArr.push({
                    "label": item.dwmc,
                    "value": item.id
                })
            })
            this.srcDeviceJson[0].options = optionsArr; 
        },
        /* 
         * getFilterDeviceOrTemplateSelectData 初始化1级下拉框选择的所有选项
         * params => object,  {jb: 1} // 代表级别 1，默认第一个选择框的数据。
        */
        getFilterDeviceOrTemplateSelectData(selectJson){
            let setOption ={...selectJson};
            let optionsJson ={}
            let maxStepNum = 5;       
            // 如果 params 有 val 这个属性，则需要设置这个查询值 {fdwdm: selectJson.val}
            if(selectJson.val){
                setOption = {
                    fdwdm: selectJson.val
                }
            }

            // 判断是调用设备筛选，还是模型筛选
            if(selectJson.sourceName.indexOf('Device')>=0){     
                // 调用筛选符合条件的设备下拉框列表的方法
                this.filterDeviceList(selectJson);                    
            }else{
                // 模型筛选
                this.getFilterTemplateList(selectJson);
            }
            
            let jsonKey =  `${selectJson.sourceName}Json`
            optionsJson = this[jsonKey] 
            // 当前操作的第几步，然后把后面的select下拉框都重新置空
            for(let i=selectJson.stepEq+1; i<maxStepNum;i++){ 
                if(i!=0){
                    optionsJson[i].options = []
                    optionsJson[i].value = ''
                }
            }
            this[jsonKey] = optionsJson; 
            // 选择完之后，重新设置select 下拉框的值 （ options 和 value ）
            // 设备或者模型，筛选接口需要传入的字段 // fdwdm: 68
            getDeviceSelectApi(setOption).then(res=>{
                // console.log(res)
                // console.log(setOption)
                // console.log('setOption =============================== ')
                // let optionsArr = [{
                //      "label": '请选择',
                //      "value": ''
                // }]
                let optionsArr = []
                res.forEach(item=>{
                    optionsArr.push({
                        "label": item.dwmc,
                        "value": item.id
                    }) 
                });
                // 如果是初始化获取第一级，则不需要修改下一级的数据。
                // 需要把下一级的数据，根据返回值，重新设置一下。有下一级，才需要设置，如果超过最大层级，则不需要设置
                let nextSelectNum = selectJson.stepEq+1; 
                if(selectJson.jb){
                    nextSelectNum = 0;
                }
                if(nextSelectNum<maxStepNum){
                    optionsJson[nextSelectNum].options = optionsArr
                    optionsJson[nextSelectNum].value = '';
                    this[jsonKey] = optionsJson;
                }                
                // 初始化设置源筛选下拉框的时候，需要吧目标筛选项也设置上
                // 初始化，模型筛筛选项，下拉框数据
                if(selectJson.jb){
                    this['targetDeviceJson'][0].options = optionsArr
                    this['targetDeviceJson'][0].value = ''
                    
                    // 设置模型的筛选项
                    this['srcTemplateJson'][0].options = optionsArr
                    this['srcTemplateJson'][0].value = ''
                    this['targetTemplateJson'][0].options = optionsArr
                    this['targetTemplateJson'][0].value = '' 
                }
            }); 
        },        
         
        // 根据下拉框的值，筛选符合条件的设备下拉选择列表
        filterDeviceList(selectJson){            
            // 定义筛选设备列表的参数对象
            let postFilterDeviceParams = {
                zyq_id:null,
                dev_azdw1_id:'',
                dev_azdw2_id:'',
                dev_azdw3_id:'',
                dev_azdw4_id:'',
                dev_azdw5_id:''
            }
            let startNum = 0;
            let jsonKey =  `${selectJson.sourceName}Json`;
            let optionsJson = this[jsonKey]
            console.log(optionsJson)
            while (startNum<=selectJson.stepEq){
                // console.log(`startNum : ${startNum}`) 
                postFilterDeviceParams[`dev_azdw${startNum+1}_id`] = optionsJson[startNum].value;
                startNum+=1;
            }

            getDeviceListApi(postFilterDeviceParams).then(res=>{
                // console.log('getDeviceListApi ====================== ')
                // console.log(res)
                // console.log(deviceOptionKey,deviceValueKey,'deviceValueKey, deviceOptionKey');
                let optionsArr = [];
                if(res.data && res.data.length>0){                    
                    res.data.forEach(item=>{
                        optionsArr.push({
                            value: item.id, 
                            label: item.dev_name,
                            deviceId: item.id, 
                            name: item.dev_name 
                        })
                    });
                }
                let deviceOptionKey = `${selectJson.sourceName}Options`;
                let deviceValueKey = `${selectJson.sourceName}Value`;
                this[deviceOptionKey] = optionsArr;
                this[deviceValueKey] = '';
                // console.log(`this[deviceOptionKey] ============================== ` )
                // console.log(this[deviceOptionKey] )
            })
        },
        /* 
         * selectOptionsJsonToList : 设备 或 模型的筛选项，根据 区 或 站 或 班 来筛选对应的设备或者模型。
         * e : 选择完select之后的默认事件对象 e
         * stepName:  操作的步骤名称，代表 区 或 站 
         * toSelectName： 选择完之后，需要影响的结果，代表 （设备 或者 模型 ） 
        */
        selectOptionsJsonToList(selectJson){
            // 设备筛选项目 选中某一项之后，获取下级（后一项） 下拉框的值
            this.getFilterDeviceOrTemplateSelectData(selectJson);
        }
    }
};
</script>
<style lang="scss">
.diagram-example-vue {
    width: 100%;
    height: 100vh;
    overflow: hidden;
    position: relative;

    .flex-1 {
        flex: 1;
    }

    .node-title {
        font-size: 14px;
        line-height: 1.6;
        font-weight: bold;
    }
    .basic-node {
        margin: 10px auto;
        background: #fff;
        color: black;
        border-radius: 15px;
        height: 30px;
        border: 1px solid #289de9;
        display: flex;
        justify-content: center;
        align-items: center;
        cursor: pointer;
        user-select: none;
        overflow: hidden;

        .icon {
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 24px;
            color: #fff;
            width: 28px;
            height: 28px;
            background: #289de9;
            border-radius: 50%;
            margin: 1px 10px 1px 1px;
        }

        .node-text {
            flex: 1;
        }
    }
    .tip-wrap {
        color: #f00;
        font-size: 12px;
        line-height: 1.4;
        padding-bottom: 15px;
    }

    .page-left {
        position: absolute;
        left: 0;
        top: 0;
        width: 140px;
        padding: 10px;
    }

    .tree-ul {
        color: #000;

        .item-text {
            padding-left: 20px;
        }
    }

    .right-form {
        width: 300px;
        position: absolute;
        right: 0;
        top: 0;
        display: flex;
        height: 100%;
        border-left: 1px solid #ccc;
        background: #fff;
        flex-direction: column;
        .title {
            font-size: 20px;
            line-height: 80px;
        }

        .config-tabs {
            display: none;
        }
        .config-tabs {
            padding: 0 20px;

            .el-tabs__nav-wrap {
                display: flex;
                justify-content: center;
            }
        }

        .form-cont {
            flex: 1;
            overflow: auto;
            padding: 0 20px;
        }
        .form-title {
            padding: 10px 20px;
            font-size: 16px;
            line-height: 1.4;
        }

        .section-title {
            display: flex;
            align-items: center;
            padding: 6px 0 4px 0;
            font-size: 14px;
            line-height: 1;
            color: #666;
        }

        .section-cont {
            padding: 0 10px;

            .select-title {
                margin-top: 8px;
                font-size: 12px;
                line-height: 1.6;
                color: #999;
            }
        }
        
        .list-title{ padding: 5px 0 2px 0; font-size: 12px; line-height: 1; color:#666; }
        .select-list-wrap{ 
            .el-select{ margin: 2px; display: inline-block; width: 105px; }
        }
        .rule-head {
            height: 30px;
            font-size: 12px;
            color: #666;
            display: flex;
            align-items: center;
        }

        .rule-text {
            min-height: 20px;
            line-height: 20px;
            font-size: 12px;
            color: #666;
            background: #f6f6f6;
            text-align: right;
        }
        .rule-input {
            display: block;
            height: 20px;
            font-size: 14px;
            background: #f6f6f6;
            text-align: right;
            border: 1px solid #333;
            box-sizing: border-box;
            width: 100%;
        }
        .rule-btn-wrap,
        .rule-head {
            display: flex;
            flex-wrap: wrap;
            .btn-item {
                margin: 2px;
                padding: 0 5px;
                min-width: 14px;
                height: 24px;
                display: inline-flex;
                justify-content: center;
                align-items: center;
                background: #409eff;
                font-size: 12px;
                color: #fff;
                cursor: pointer;
            }
            .clear-btn {
                background: #96b97d;
            }
            .save-btn {
                background: #96b97d;
            }
            .symbol-btn {
                font-size: 18px;
            }
        }

        .save-basic-btn {
            display: block;
            margin: 20px auto;
        }

        .tab-cont {
            height: 500px;
            overflow-y: auto;
        }
    }
}

/** 给svg添加格子背景，这个是拖拽的svg画布 */
#svgContent {
    background-size: 50px 50px;
    background-image: linear-gradient(
            0deg,
            transparent 24%,
            rgba(255, 255, 255, 0.05) 25%,
            rgba(255, 255, 255, 0.05) 26%,
            transparent 27%,
            transparent 74%,
            rgba(255, 255, 255, 0.05) 75%,
            rgba(255, 255, 255, 0.05) 76%,
            transparent 77%,
            transparent
        ),
        linear-gradient(
            90deg,
            transparent 24%,
            rgba(255, 255, 255, 0.05) 25%,
            rgba(255, 255, 255, 0.05) 26%,
            transparent 27%,
            transparent 74%,
            rgba(255, 255, 255, 0.05) 75%,
            rgba(255, 255, 255, 0.05) 76%,
            transparent 77%,
            transparent
        );
    background-color: rgb(60, 60, 60) !important;
    margin-left: 160px;
    margin-right: 200px;
}
</style>
